#include "stdafx.h"
#include "XFixedSizeAllocator.h"

#include "CXcpAutoCritSec.h"
#include "IPALThreadingServices.h"



XFixedSizeAllocator::XFixedSizeAllocator()
{
	this->m_pcsBlocks = NULL;
}

XFixedSizeAllocator::~XFixedSizeAllocator()
{
	XUINT32 dummy = 0;

	this->Cleanup(dummy);

	XASSERT(dummy == 0);
}

HRESULT XFixedSizeAllocator::Initialize( XUINT32 uItemSize, XUINT32 uSlotsPerBlock, XUINT32 uMaxNumberOfBlocks, XUINT32 uInitialNumberOfBlocks )
{
	XUINT32 i;
	HRESULT hr = S_OK;

	XASSERT_SUCCESSED(GetPALThreadingServices()->CriticalSectionCreate(&this->m_pcsBlocks));

	CXcpAutoCritSec csLock(this->m_pcsBlocks);

	this->m_uItemSize = uItemSize;
	this->m_uSlotsPerBlock = uSlotsPerBlock;
	this->m_uMaxNumberOfBlocks = uMaxNumberOfBlocks;
	if ( uMaxNumberOfBlocks && uMaxNumberOfBlocks < XFixedSizeAllocator::INITIAL_BLOCK_ARRAY_SIZE )
		this->m_uNumberOfBlocks = uMaxNumberOfBlocks;
	else
		this->m_uNumberOfBlocks = XFixedSizeAllocator::INITIAL_BLOCK_ARRAY_SIZE;


	XASSERT_NOTNULL_WITH_HR(this->m_pBlocks = new _XContiguousBlock*[m_uNumberOfBlocks],0x8007000Eu);

	memset(this->m_pBlocks, NULL, sizeof(_XContiguousBlock*) * this->m_uNumberOfBlocks);

	for ( i = 0; i<uInitialNumberOfBlocks; i++ )
	{
		XASSERT_NOTNULL_WITH_HR(this->m_pBlocks[i] = new _XContiguousBlock(),0x8007000Eu);
		XASSERT_SUCCESSED(this->m_pBlocks[i]->Initialize(this->m_uItemSize, uSlotsPerBlock));
	}
	return hr;
}

void XFixedSizeAllocator::Cleanup( XUINT32 & outLeakedItems )
{
	XUINT32 uLeakedInThisBlock;
	XUINT32 i;

	outLeakedItems = 0;

	if ( this->m_pcsBlocks )
	{
		CXcpAutoCritSec csLock(this->m_pcsBlocks);

		for ( i = 0; i < this->m_uNumberOfBlocks && this->m_pBlocks[i]; ++i )
		{
			uLeakedInThisBlock = 0;
			this->m_pBlocks[i]->Cleanup(uLeakedInThisBlock);
			outLeakedItems += uLeakedInThisBlock;

			if(this->m_pBlocks[i])
			{
				delete this->m_pBlocks[i];
			}
			this->m_pBlocks[i] = NULL;
		}

		delete[] this->m_pBlocks;

		this->m_pBlocks = NULL;
		this->m_uNumberOfBlocks = 0;
		this->m_uSlotsPerBlock = 0;
		this->m_uMaxNumberOfBlocks = 0;
		this->m_uItemSize = 0;
		this->m_pcsBlocks->Delete();
		this->m_pcsBlocks = NULL;
	}
}

XUINT8 * XFixedSizeAllocator::Allocate()
{
	XRESULT hr = S_OK;
	XUINT8 *pReturnedBuffer = NULL;
	XUINT32 uPreviousBlockCount;


	XASSERT(m_pcsBlocks);

	if ( this->m_pcsBlocks )
	{
		CXcpAutoCritSec csLock(this->m_pcsBlocks);

		for (XUINT32 i = 0; i < this->m_uNumberOfBlocks; i++ )
		{
			if ( !this->m_pBlocks[i] )
			{
				XASSERT_NOTNULL_FAILED_GOTO(this->m_pBlocks[i] = new _XContiguousBlock(),CleanAndExit);
				XASSERT_FAILED_GOTO(this->m_pBlocks[i]->Initialize(this->m_uItemSize, this->m_uSlotsPerBlock),CleanAndExit);
			}
			pReturnedBuffer = this->m_pBlocks[i]->Allocate();
			if ( pReturnedBuffer )
			{
				return pReturnedBuffer;
			}
		}


		if ( !this->m_uMaxNumberOfBlocks || this->m_uNumberOfBlocks < this->m_uMaxNumberOfBlocks )
		{
			uPreviousBlockCount = this->m_uNumberOfBlocks;
			if ( this->GrowBlockArray() >= 0 )
			{
				pReturnedBuffer = this->m_pBlocks[uPreviousBlockCount]->Allocate();
				return pReturnedBuffer;
			}
		}
	}
CleanAndExit:
	return NULL;
}

HRESULT XFixedSizeAllocator::Release( XUINT8 * pBufferToFree )
{
	HRESULT hr = 0x8000FFFFu;
	XASSERT(m_pcsBlocks);
	if ( this->m_pcsBlocks )
	{
		CXcpAutoCritSec csLock(this->m_pcsBlocks);
		for (XUINT32 i = 0; i<m_uNumberOfBlocks; i++ )
		{
			if ( !this->m_pBlocks[i] )
			{
				return 0x8000FFFFu;
			}

			XASSERT_SUCCESSED(m_pBlocks[i]->Release(pBufferToFree));
			return S_OK;
		}
	}
	return hr;
}

HRESULT XFixedSizeAllocator::GrowBlockArray()
{
	HRESULT hr = S_OK;
	XUINT32 i;
	_XContiguousBlock ** pNewBlockArray;

	XASSERT_NOTNULL_WITH_HR(m_uNumberOfBlocks <= XUINT32_MAX - m_uNumberOfBlocks,0x8000FFFFu);

	XUINT32 uNewBlockCount = 2 * this->m_uNumberOfBlocks;


	if ( this->m_uMaxNumberOfBlocks && uNewBlockCount > this->m_uMaxNumberOfBlocks )
	{
		hr = 0x8000FFFFu;
		if ( GetPALDebuggingServices()->GetTraceFlags() & 2 )
		{
			GetPALDebuggingServices()->XcpTrace(
				8u,
				L"windows\\dxaml\\xcp\\pal\\common\\xfixedsizeallocator.cpp",
				147,
				0x8000FFFFu,
				L"(m_uMaxNumberOfBlocks == 0) || (uNewBlockCount <= m_uMaxNumberOfBlocks)",
				0);
		}
	}
	else
	{

		XASSERT_NOTNULL_WITH_HR(pNewBlockArray = new _XContiguousBlock*[uNewBlockCount],0x8007000Eu);

		memset((char *)pNewBlockArray + sizeof(_XContiguousBlock*) * this->m_uNumberOfBlocks, 0, sizeof(_XContiguousBlock*) * this->m_uNumberOfBlocks);
		memcpy(pNewBlockArray, this->m_pBlocks, sizeof(_XContiguousBlock*) * this->m_uNumberOfBlocks);


		for ( i = this->m_uNumberOfBlocks;i< uNewBlockCount;i++ )
		{
			XASSERT_NOTNULL_WITH_HR(pNewBlockArray[i] = new _XContiguousBlock(),0x8007000Eu);
			XASSERT_SUCCESSED(pNewBlockArray[i]->Initialize(this->m_uItemSize,this->m_uSlotsPerBlock));
		}

		delete[] this->m_pBlocks;

		this->m_pBlocks = NULL;
		this->m_pBlocks = pNewBlockArray;
		this->m_uNumberOfBlocks = uNewBlockCount;
	}

	return hr;
}

const XUINT32 XFixedSizeAllocator::INITIAL_BLOCK_ARRAY_SIZE = 8;
